/****************************************************************************
 * Copyright 2009 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.filter.common;

import com.gotobject.filter.FilterImpl;
import static com.gotobject.filter.common.Predicates.all;
import static com.gotobject.filter.common.Initializers.Range.newIterable;
import com.gotobject.filter.predicate.Predicate;

import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class Initializers {
	private Initializers(){}

    public static <T extends Object & Comparable<? super T>> Iterable<T> range(T start, T end){
        return range(start, end, all());
    }

    public static <T extends Object & Comparable<? super T>> Iterable<T> range(T start, T end, Predicate<? super T> cond){
        return rangeImpl(start, end, cond);
    }

    @SuppressWarnings({"unchecked"})
    private static <T> Iterable<T> rangeImpl(T start, T end, Predicate<? super T> cond){
        return newIterable(start, end, cond);
    }

    public static <T> Iterable<T> repeat(T n, int times){
        return new Repeat<T>(n, times).select();
    }


    /**
     * convinient class to repeat a number {@code n} a certain amount of {@code times}.
     */
    public static class Repeat <T> {
        private final T     n;
        private final int   times;

        Repeat(T n, int times){
            this.n      = n;
            this.times  = times;
        }

        public Iterable<T> select(){
            return new FilterImpl().from(new RepeatIterableFactory<T>(n, times)).select();
        }
    }

    /**
     * convinient class to creating a range of numbers given
     * a {@code start} & {@code end} bounds.
     */
	public static class Range {
        private final int    start;
        private final int    end;

		private Range(int start, int end) {
            this.start  = start;
            this.end    = end;
        }

        private Character[] charArray(){
            final Character[] result = new Character[(end - start) + 1];
            int count = 0;
            for(int idx = start; idx <= end; idx++){
               result[count] = (char)(idx);
               count++;
            }

            return result;
        }

        private static void checkSupportedNumericTypes(Object start, Object end){
            final boolean isInteger   = ((start instanceof Integer) && (end instanceof Integer));
            final boolean isLong      = ((start instanceof Long) && (end instanceof Long));
            final boolean isShort     = ((start instanceof Short) && (end instanceof Short));
            final boolean isDouble    = ((start instanceof Double) && (end instanceof Double));
            final boolean isFloat     = ((start instanceof Float) && (end instanceof Float));

            if(!isInteger && !isLong && !isShort && !isDouble && !isFloat) {
                throw new IllegalArgumentException("type: not supported.");
            }
        }

        @SuppressWarnings({"unchecked"})
        static <T> Iterable<T> newIterable(Object start, Object end, Predicate<? super T> cond){
            final boolean isCharacter = ((start instanceof Character) && (end instanceof Character));
            if(!isCharacter) {
                checkSupportedNumericTypes(start, end);
            }

            return (Iterable<T>) (isCharacter
                    ? newCharacterRange(start, end).ofChars((Predicate<? super Character>) cond)
                    : newNumericRange(start, end).ofIntegers((Predicate<? super Integer>) cond)
            );
        }

        private static Range newCharacterRange(Object start, Object end){
            return new Range((Character)start, (Character)end);
        }

        private static Range newNumericRange(Object start, Object end){
            return new Range((Integer)start, (Integer)end);
        }

        public Iterable<Character> ofChars(Predicate<? super Character> cond){
            return new FilterImpl().from(Arrays.asList(charArray())).where(cond).select();
        }

        public Iterable<Integer> ofIntegers(Predicate<? super Integer> cond){
            return new FilterImpl().from(new RangeIterableFactory(start, end)).where(cond).select();
        }

    }

    /**
     * convinient iterable class to creating a range of numbers given
     * a {@code start} & {@code end} bounds.
     */
    private static class RangeIterableFactory implements Iterable<Integer> {
        private final int end;
        private final int start;

        public RangeIterableFactory(int start, int end) {
            this.end    = end;
            this.start  = start;
        }

        public Iterator<Integer> iterator() {
            return new RangeIterator(start, end);
        }
    }

    /**
     * convinient iterator class to creating a range of numbers given
     * a {@code start} & {@code end} bounds.
     */
    private static class RangeIterator implements Iterator<Integer> {
        private final int stop;
        private       int cur;

        public RangeIterator(int start, int stop) {
            this.cur  = start;
            this.stop = stop;
        }

        public boolean hasNext() {
                return cur < stop;
        }

        public Integer next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            final int ret = cur;
            cur++;
            return ret;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }


    /**
     * convinient iterable class to repeat a number {@code n} a certain amount of {@code times}.
     */
    private static class RepeatIterableFactory<T> implements Iterable<T> {
        private final T     n;
        private final int   times;

        RepeatIterableFactory(T n, int times) {
            this.n      = n;
            this.times  = times;
        }

        public Iterator<T> iterator() {
            return new RepeatIterator<T>(n, times);
        }
    }

    /**
     * convinient iterator class to repeat a number {@code n} a certain amount of {@code times}.
     */
    private static class RepeatIterator<T> implements Iterator<T> {
        private final int times;
        private final T   n;
        private       int cur;

        public RepeatIterator(T n, int times) {
            this.n      = n;
            this.times  = times;
            cur         = 0;
        }

        public boolean hasNext() {
                return cur < times;
        }

        public T next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            cur++;
            return n;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
